package com.yihaodian.search.code.service.impl;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.xwork.ArrayUtils;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc.SVNInfo;

import com.google.gson.Gson;
import com.yihaodian.api.client.SearchClient;
import com.yihaodian.api.index.IndexDoc;
import com.yihaodian.api.index.IndexRequest;
import com.yihaodian.configcentre.md5.MD5;
import com.yihaodian.search.code.manage.SourceIndexManager;
import com.yihaodian.search.code.model.SourceCodeEntry;
import com.yihaodian.search.code.model.UpdateCodeEntry;
import com.yihaodian.search.code.service.SourceIndexService;
import com.yihaodian.search.code.util.SvnUtil;

public class SourceIndexImpl implements SourceIndexService {

    private static final int MAX_INDEX_COUNT = 10;
    private static SearchClient searchClient = new SearchClient();
//    private String[] indexFileType = {"xml", "js", "jsp", "java", "txt", "ini", "properties", "htm", "html"};
    private String[] indexFileType = {"js", "java", "txt", "ini", "properties", "htm"};
    private MD5 md5 = MD5.getInstance();

	@Override
	public boolean dumpSources(String svnRepository, String localPath, String projectName) {
		File checkOutPath = new File(localPath);
		if (checkOutPath.exists()) {//update
			boolean success = SvnUtil.doUpdate(checkOutPath, false, null);
			if(!success)
				return false;
		} else {//checkout
			boolean success = SvnUtil.doCheckOut(checkOutPath, svnRepository);
			if(!success)
				return false;
		}
		
		System.out.println("indexing " + svnRepository);
		List<SourceCodeEntry> codeEntries = new ArrayList<SourceCodeEntry>();
		indexSvnFile(checkOutPath, codeEntries, projectName);
		
		IndexRequest indexRequest = new IndexRequest();
		indexRequest.setIndexName(INDEX_NAME);
		Set<IndexDoc> indexDocs = new HashSet<IndexDoc>();
		int cnt = 0;
		for (SourceCodeEntry sourceCodeEntry : codeEntries) {
			if(indexDocs.size() >= MAX_INDEX_COUNT)
			{
				indexRequest.setDocs(indexDocs);
//				System.out.println(new Gson().toJson(indexRequest));
				try
				{
					searchClient.index(indexRequest);
					System.out.println(cnt += indexDocs.size());
					indexDocs.clear();
				} catch(Exception e)
				{
					e.printStackTrace();
					try {
						Thread.sleep(3 * 1000);
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
				}
			}
			
			IndexDoc indexDoc = sourceCodeEntry2IndexDoc(sourceCodeEntry);
			if(indexDoc == null) {
				continue;
			}
			indexDocs.add(indexDoc);
			System.out.println(sourceCodeEntry.getId());
		}
		
		if(indexDocs.size() > 0)
		{
			indexRequest.setDocs(indexDocs);
			searchClient.index(indexRequest);
		}
		
		return true;
	}

	@Override
	public boolean updateSources(String svnRepository, String localPath, String projectName) {
		File checkOutPath = new File(localPath);
		if(!checkOutPath.exists())
			return dumpSources(svnRepository, localPath, projectName);
		
		List<UpdateCodeEntry> updateCodeList = new ArrayList<UpdateCodeEntry>();
		boolean success = SvnUtil.doUpdate(checkOutPath, true, updateCodeList);
		if(!success)
			return false;
		
		
		IndexRequest indexRequest = new IndexRequest();
		indexRequest.setIndexName(INDEX_NAME);
		Set<IndexDoc> indexDocs = new HashSet<IndexDoc>();
		for (UpdateCodeEntry updateCodeEntry : updateCodeList) {
			if(indexDocs.size() >= MAX_INDEX_COUNT)
			{
				indexRequest.setDocs(indexDocs);
				searchClient.index(indexRequest);
				indexDocs.clear();
			}
			
			IndexDoc indexDoc = updateCodeEntryDoc2IndexDoc(updateCodeEntry, projectName);
			if(indexDoc == null)
				continue;
			
			indexDocs.add(indexDoc);
		}
		return true;
	}
	
	private void indexSvnFile(File svnPath, List<SourceCodeEntry> codeEntries, String projectName) {
		Collection<File> codeCollection = listFiles(svnPath, indexFileType, true);
		
		for (File file : codeCollection) {
			if(file.isHidden()) {
				continue;
			}
			
//			if (file.getPath().contains("search-code") || file.getPath().contains("search-mars")) 
			{
			
				SVNInfo svnInfo = SvnUtil.getSvnFileInfo(file);
				codeEntries.add(convertCodeEntry(file, svnInfo, projectName));
			}
		}
	}

	private SourceCodeEntry convertCodeEntry(File file, SVNInfo svnInfo, String projectName) {
		SourceCodeEntry codeEntry = new SourceCodeEntry();
		codeEntry.setId(md5.getMD5String(svnInfo.getFile().getPath()));
		
		codeEntry.setFileName(file.getName());
		codeEntry.setFileType(file.getName().substring(file.getName().lastIndexOf(".") + 1));
		codeEntry.setLastUpdateTime(svnInfo.getCommittedDate().getTime());
		codeEntry.setProjectName(projectName);
		
		String path = svnInfo.getFile().getPath();
		path = path.replace(SourceIndexManager.baseDir, "");
		codeEntry.setRelativPath(path);
		codeEntry.setRevision(svnInfo.getCommittedRevision().toString());
		codeEntry.setAuthor(svnInfo.getAuthor());
		try {
			codeEntry.setContent(FileUtils.readFileToString(file));
		} catch (IOException e) {
			e.printStackTrace();
		}
		return codeEntry;
	}
	
//	private String parseProjectName(String url){
//		if (StringUtils.isEmpty(url)) {
//			return url;
//		} else {
//			if (url.contains("search-code")) {
//				return "search-code";
//			} else {
//				return "search-mars";
//			}
//		}
//	}
	
	private IndexDoc sourceCodeEntry2IndexDoc(SourceCodeEntry codeEntry) {
		if(codeEntry == null)
			return null;
		
		IndexDoc indexDoc = new IndexDoc(codeEntry.getRelativPath(), false);
		indexDoc.setJsonDoc(new Gson().toJson(codeEntry));
		return indexDoc;
	}
	
	private IndexDoc updateCodeEntryDoc2IndexDoc(UpdateCodeEntry codeEntry, String projectName) {
		if(codeEntry == null)
			return null;

		File file = codeEntry.getFile();
		if(file.isHidden())
			return null;
		
		String fileType = file.getName().substring(file.getName().lastIndexOf(".") + 1);
		if(!ArrayUtils.contains(indexFileType, fileType))
			return null;
		
		SVNInfo svnInfo = SvnUtil.getSvnFileInfo(file);
		if(codeEntry.getAction() == SVNEventAction.DELETE)
			return new IndexDoc(svnInfo.getFile().getPath(), true);
		else {
			IndexDoc indexDoc = new IndexDoc(svnInfo.getFile().getPath(), false);
			indexDoc.setJsonDoc(new Gson().toJson(convertCodeEntry(file, svnInfo, projectName)));
			return indexDoc;
		}
		
	}
	
    @SuppressWarnings("unchecked")
	public static Collection<File> listFiles(File file, String[] extensions, boolean recursive)
    {
        if (null == file) return Collections.emptyList();
        if (file.isFile())
        {
            List<File> files = new ArrayList<File>();
            files.add(file);
            return files;
        }
        return FileUtils.listFiles(file, extensions, recursive);
    }
	


	public static void main(String[] args) {
//		new SourceIndexImpl().dumpSources(svnRepository);
//		new SourceIndexImpl().updateSources(svnRepository);
	}

}
